import { BrowserTitle, useCurrentMenu, useForceUpdate, useQueryValue, useSimpleMemo } from '@/components';
import NotFound from '@/pages/common/errors/NotFound';
import { getToken, pathJoin } from '@/utils';
import { Spin } from 'antd';
import React, { createContext, lazy, memo, Suspense, useContext, useEffect, useState } from 'react';
import { Redirect, Route, Switch, useHistory, useLocation } from 'react-router-dom';

/**
 * 从页面组件获取title静态属性作为document.title
 * @param {React.ElementType} component   页面组件
 * @return {JSX.Element}
 */
function PageTitle({ component }) {
  const matchedMenu = useMatchedMenu();
  const menuTitle = useSimpleMemo(matchedMenu?.resourceName ?? null);
  const [title, setTitle] = useState(component?._result?.title ?? null);
  useEffect(() => {
    if (typeof component?._result?.then !== 'function') return;
    let unmounted = false;
    component?._result?.then((comp) => {
      if (!unmounted) setTitle(comp?.default?.title ?? null);
    });
    return () => {
      unmounted = true;
    };
  }, []);
  return <BrowserTitle title={title ?? menuTitle} />;
}

Iframe = memo(Iframe);

function Iframe({ url }) {
  return <iframe src={url + pathJoin({ token: getToken() })} />;
}

// 路径名不包含common、component的jsx文件
// 页面私有组件可放在页面文件夹的component文件夹中
let updateRoutes;
let pages;
let context;
getPages();
// 初始化HMR
if (module.hot) {
  module.hot.accept(context.id, getPages);
}

/**
 * 初始化路由，获取pages文件夹中所有jsx文件作为页面
 * 路径中不包含common,component
 * 支持HMR
 */
function getPages() {
  context = require.context('@/pages', true, /^(?!\.\/common)((?!component).)*\.jsx$/i, 'lazy');
  let paths = [];
  pages = context.keys().map((pathname) => {
    const path = pathname.replace(/(^\.)|((\/index)?\.jsx$)/g, '');
    const isFolder = pathname.endsWith('/index.jsx');
    if (paths.includes(path) && process.env.NODE_ENV !== 'production') {
      console.warn(`匹配到重复的路径：${pathname}`);
    }
    paths.push(path);

    // 懒加载页面组件
    const Page = lazy(() => context(pathname));

    // 包裹loading效果和捕获异常
    const component = (props) => {
      return (
        <Suspense fallback={<Spin />}>
          <PageTitle component={Page} />
          <Page {...props} />
        </Suspense>
      );
    };

    return {
      pathname,
      isFolder,
      path,
      component,
    };
  });
  pages = pages.sort((a, b) => b.path.length - a.path.length);
  if (updateRoutes) updateRoutes();
}

/**
 * @type {React.Context<{matchedMenu: MatchedMenu}>}
 */
const RouteConfigContext = createContext({ matchedMenu: null });

/**
 * 获取当前路由匹配到的菜单节点
 * @return {MatchedMenu}
 */
export function useMatchedMenu() {
  return useContext(RouteConfigContext).matchedMenu;
}

/**
 * 主路由
 * @param {ResourceTree} menuTree
 * @param {String} parentPath         前缀
 * @param {Boolean} checkSubRoutes    是否做权限校验
 * @param children
 * @return {JSX.Element}
 */
const RouteConfig = ({ menuTree, parentPath = '/', checkSubRoutes = false }) => {
  updateRoutes = useForceUpdate();
  const currentMenu = useCurrentMenu(menuTree, { prefix: parentPath, exact: checkSubRoutes });
  const locationIframe = useQueryValue('iframe');

  // 是否做权限校验 (当前url必须在resource配置中)
  if (!currentMenu && checkSubRoutes) {
    return <div style={{ textAlign: 'center' }}>无操作权限</div>;
  }

  const location = useLocation();
  const history = useHistory();
  useEffect(() => {
    const safePath = location.pathname.replace(/\/{2,}/g, '/');
    console.log({
      l1: location.pathname,
      safePath,
    });
    if (safePath !== location.pathname) {
      history.replace({
        ...location,
        pathname: safePath,
      });
    }
  }, [location]);

  return (
    <RouteConfigContext.Provider value={{ matchedMenu: currentMenu }}>
      <Switch>
        <Route path='/' exact>
          <Redirect to='/product/product/BasicProductsPage/' />
        </Route>
        <Route path='/404' exact component={NotFound} />
        {locationIframe && <Iframe url={locationIframe} />}
        {!!currentMenu?.iframeUrl && <Iframe url={currentMenu?.iframeUrl} />}
        {pages.reduce(
          (ret, route) =>
            route.isFolder && route.path
              ? [
                  ...ret,
                  <Route
                    key={route.path}
                    path={pathJoin(parentPath, route.path + '/')}
                    strict
                    component={route.component}
                  />,
                  <Redirect
                    key={`${route.path}.redirect`}
                    from={pathJoin(parentPath, route.path)}
                    to={pathJoin(parentPath, route.path + '/')}
                  />,
                ]
              : [
                  ...ret,
                  <Route
                    key={route.path}
                    path={pathJoin(parentPath, route.path)}
                    exact={checkSubRoutes}
                    component={route.component}
                  />,
                ],
          [],
        )}
        {!currentMenu?.iframeUrl && <Redirect to='/404' />}
      </Switch>
    </RouteConfigContext.Provider>
  );
};

export default RouteConfig;
